package torchtools.gui;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.prefs.Preferences;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.SpinnerNumberModel;
import javax.swing.filechooser.FileSystemView;
import torchtools.binary.TLPlayerSave;
import torchtools.io.IOUtil;

/**
 *
 * @author Matt
 */
public abstract class BaseTool extends JFrame implements ToolContext, ActionListener {

    /** Note that prefs are shared between 'mini' tools. */
    protected static Preferences prefs = Preferences.userNodeForPackage(BaseTool.class);
    
    private static final String[] browsers = { "firefox", "opera", "konqueror", 
        "epiphany", "seamonkey", "galeon", "kazehakase", "mozilla", "netscape"
    };
    
    protected TLPlayerSave save;
    protected JFileChooser jfc = null;
    protected SaveFileChooser saveChooser = null;
    protected File saveDir;
    
    protected boolean updateSaveChooser = false;
    protected boolean needsReload = false;
    
    //default menu bar
    protected JMenuBar menuBar;
    protected JMenu fileMenu;
    protected JMenuItem openMenuItem;
    protected JMenuItem saveMenuItem;
    protected JMenuItem closeMenuItem;
    protected JSeparator jSeparator1;
    protected JMenuItem backupMenuItem;
    protected JMenuItem quitMenuItem;
    protected JMenu helpMenu;
    protected JMenuItem aboutMenuItem;
    protected JMenuItem helpMenuItem;
       
    public BaseTool() {
        String saveDirPath = getPrefs().get("saveDir", null);
        if (saveDirPath!=null)
            saveDir = new File(saveDirPath);
        
        WindowListener l = createWindowListener();
        if (l!=null)
            addWindowListener(l);
        
        initMenuBarActions();
        initMenuBarLayout();
    }
    
    public Preferences getPrefs() {
        return BaseTool.prefs;
    }
    
    /** Receives events from the default menu bar. */
    public void actionPerformed(ActionEvent ev) {
        Object o = ev.getSource();
        
        if (o == openMenuItem) {
            openFileAction(ev);
        } else if (o == saveMenuItem) {
            saveFileAction(ev);
        } else if (o == closeMenuItem) {
            closeFileAction(ev);
        } else if (o == backupMenuItem) {
            backupAction(ev);
        } else if (o == quitMenuItem) {
            quitAction(ev);
        } else if (o == aboutMenuItem) {
            aboutAction(ev);
        } else if (o == helpMenuItem) {
            helpAction(ev);
        }
    }
    
    protected void quitAction(ActionEvent e) {
        cleanup();
        System.exit(0);
    }
    
    protected void closeFileAction(ActionEvent e) {
        clear();
    }
    
    protected void openFileAction(ActionEvent ev) {
        File f = showSaveChooser();
        if (f!=null) {
            try {
                openFile(f);
            } catch (IOException e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, "Error loading save file: " +
                        e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }
    
    protected void backupAction(ActionEvent ev) {
        try {
            File dir = getSaveDir();
            IOUtil.copyDir(dir, new File("backup"), SaveFileChooser.SVT_FILTER);
        } catch (IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Error backing up save dir: " +
                    e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            clear();
        }
    }
    
    protected void aboutAction(ActionEvent ev) {
        String msg = "<html>Another Mini Tool -- created by mattdesl.</html>";
        JOptionPane.showMessageDialog(this, msg, "About", JOptionPane.PLAIN_MESSAGE);
    }
    
    protected void helpAction(ActionEvent ev) {
    }
    
    protected void saveFileAction(ActionEvent ev) {
        if (save==null)
            return;
        try {
            if (!saveFile()) 
                JOptionPane.showMessageDialog(this, "There was a problem saving:" +
                        " the file may be in use. Close the tool and try again.", 
                        "Error", JOptionPane.ERROR_MESSAGE);
        } catch (IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Error saving file: " +
                    e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        }
    }
        
    /** Initializes text, icons accelerators and actions for the default menu bar. */
    protected void initMenuBarActions() {
        menuBar = new JMenuBar();
        fileMenu = new JMenu();
        openMenuItem = new JMenuItem();
        saveMenuItem = new JMenuItem();
        closeMenuItem = new JMenuItem();
        jSeparator1 = new JSeparator();
        backupMenuItem = new JMenuItem();
        quitMenuItem = new JMenuItem();
        helpMenu = new JMenu();
        aboutMenuItem = new JMenuItem();
        helpMenuItem = new JMenuItem();
        
        fileMenu.setText("File");
        openMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        openMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/open.png"))); // NOI18N
        openMenuItem.setText("Open");
        openMenuItem.addActionListener(this);

        saveMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        saveMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/save.png"))); // NOI18N
        saveMenuItem.setText("Save");
        saveMenuItem.addActionListener(this);
        
        closeMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_W, java.awt.event.InputEvent.CTRL_MASK));
        closeMenuItem.setText("Close File");
        closeMenuItem.addActionListener(this);
        
        backupMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_B, java.awt.event.InputEvent.CTRL_MASK));
        backupMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/backup.png"))); // NOI18N
        backupMenuItem.setText("Backup Saves");
        backupMenuItem.addActionListener(this);
        
        quitMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK));
        quitMenuItem.setText("Quit");
        quitMenuItem.addActionListener(this);
        
        helpMenu.setText("Help");
        
        aboutMenuItem.setText("About");
        aboutMenuItem.addActionListener(this);
        
        helpMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F1, 0));
        helpMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/help.png"))); // NOI18N
        helpMenuItem.setText("Help Contents");
        helpMenuItem.addActionListener(this);
    }
    
    /** Adds items to the menu bar. Override to change behaviour. */
    protected void initMenuBarLayout() {
        fileMenu.add(openMenuItem);
        fileMenu.add(saveMenuItem);
        fileMenu.add(closeMenuItem);
        fileMenu.add(jSeparator1);
        fileMenu.add(backupMenuItem);
        fileMenu.add(quitMenuItem);
        menuBar.add(fileMenu);
        
        helpMenu.add(aboutMenuItem);
        helpMenu.add(helpMenuItem);
        menuBar.add(helpMenu);
        
        setJMenuBar(menuBar);
    }
    
    /** Either set to null or override for custom window closing events. */
    protected WindowListener createWindowListener() {
        return new WindowAdapter() {
            private void formWindowClosing(WindowEvent evt) {                                
                cleanup();
                System.exit(0);
            }
        };
    }
    
    public JFrame getFrame() {
        return this;
    }
    
    public void resetSaveDir() {
        setSaveDir(null);
    }
    
    public File getSaveDir() {
        if (saveDir==null) {
            //Tries to use the default data path, returns null if it can't be found
            String appdata = System.getenv("APPDATA");
            if (appdata!=null) {
                String dir = appdata+"\\runic games\\torchlight\\save";
                File f = new File(dir);
                if (f.exists())
                    saveDir = f;
            }
        }
        return saveDir;
    }
    
    public void setSaveDir(File dir) {
        saveDir = dir;
        if (dir==null)
            getPrefs().remove("saveDir");
        else
            getPrefs().put("saveDir", dir.getPath());
    }
    
    public JFileChooser getFileChooser(String startDir) {
        if (jfc==null) {
            String dir = getPrefs().get("jfc.dir", startDir);
            System.out.println(dir);
            File f = dir==null 
                        ? FileSystemView.getFileSystemView().getDefaultDirectory()
                        : new File(dir);
            jfc = new JFileChooser(dir);
            jfc.setAcceptAllFileFilterUsed(true);
            
        }
        return jfc;
    }
    
    public SaveFileChooser getSaveChooser() {
        if (saveChooser==null)
            saveChooser = new SaveFileChooser(this);
        return saveChooser;
    }
    
    public File showSaveChooser() {
        SaveFileChooser c = getSaveChooser();
        if (updateSaveChooser) {
            c.saveDirChanged();
            updateSaveChooser = false;
        }
        return c.showDialog();
    }
    
    protected void cleanup() {
        if (save!=null)
            closeFile();
        if (jfc!=null) {
            File f = jfc.getSelectedFile();
            if (f!=null)
                getPrefs().put("jfc.dir", f.getParent());
        }
        if (saveDir!=null)
            getPrefs().put("saveDir", saveDir.getPath());
    }
    
    /** Closes the save file stream. */
    protected void closeFile() {
        if (save!=null) {
            try { save.close(); }
            catch (IOException e) {}
        }
    }
    
    /** Reloads the file -- override for specific reading. */
    protected void reloadFile(File path) throws IOException {
        closeFile();
        save = createPlayerSave(path);
        needsReload = false;
    }
    
    protected TLPlayerSave createPlayerSave(File path) throws IOException {
        return new TLPlayerSave(path, "rw");
    }
    
    /** Closes the save file and clears the GUI. */
    protected void clear() {
        closeFile();
        save = null;
    }
    
    protected abstract boolean saveFileImpl() throws IOException;
    
    protected boolean saveFile() throws IOException {
        if (save==null)
            return true;
        if (needsReload)
            reloadFile(save.getFile());
        
        boolean success = saveFileImpl();
        
        save.close();
        needsReload = true;
        updateSaveChooser = true;
        return success;
    }
    
    //opens the save file
    protected void openFile(File path) throws IOException {
        reloadFile(path);
    }
    
    /**
     * Opens the specified web page in a web browser
     * 
     * <b>Bare Bones Browser Launch for Java</b><br>
     * Utility class to open a web page from a Swing application
     * in the user's default browser.<br>
     * Supports: Mac OS X, GNU/Linux, Unix, Windows XP/Vista<br>
     * Example Usage:<code><br> &nbsp; &nbsp;
     *    String url = "http://www.google.com/";<br> &nbsp; &nbsp;
     *    BareBonesBrowserLaunch.openURL(url);<br></code>
     * Latest Version: <a href="http://www.centerkey.com/java/browser/">www.centerkey.com/java/browser</a><br>
     * Author: Dem Pilafian<br>
     * Public Domain Software -- Free to Use as You Like
     *
     * @param url A web address (URL) of a web page (ex: "http://www.google.com/")
     * @version 2.0, May 26, 2009
     */
    public void openURL(String url) {
        String osName = System.getProperty("os.name");
        try {
            if (osName.startsWith("Mac OS")) {
                Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
                Method openURL = fileMgr.getDeclaredMethod("openURL",
                        new Class[]{String.class});
                openURL.invoke(null, new Object[]{url});
            } else if (osName.startsWith("Windows")) {
                Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
            } else { //assume Unix or Linux
                boolean found = false;
                for (String browser : browsers) {
                    if (!found) {
                        found = Runtime.getRuntime().exec(
                                new String[]{"which", browser}).waitFor() == 0;
                        if (found) {
                            Runtime.getRuntime().exec(new String[]{browser, url});
                        }
                    }
                }
                if (!found) {
                    throw new Exception(Arrays.toString(browsers));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Error attempting to launch " +
                    "web browser\n" + e.toString(), "Error", 
                    JOptionPane.ERROR_MESSAGE);
        }
    }
}
